# Copyright (C) 2005-2006 Frederic Back <fredericback@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

import string
import sys, csv
from cStringIO import StringIO
from operator import itemgetter
from codecs import *

from mezoGISlib._plugins import Argument, UserPlugin
from mezoGISlib import getGeometryExtent, Table

def register():
    return ImportDbf()

class ImportDbf(UserPlugin):

    def __init__(self):
        UserPlugin.__init__(self, "Import DBF",
        "Import a dbase table",
        "Import a dbase table.")
        self.arguments = {
            "input file": Argument(Argument.tfileopen,"the path of the dbf file to import","table.dbf"),
            "output table": Argument(Argument.tstring,"the name of the table to create","imported_dbf")
        }

    def getInput(self):
        return self.arguments

    def launch(self, database):
        filename = self.arguments["input file"].value
        f = open(filename, 'rb')
        db = list(dbfreader(f))
        f.close()

        columnNames = db.pop(0)
        columnData = db.pop(0)

        def convertData( t, s, d ):
            if t == 'N' or t == 'I':
                if d > 0: return "real"
                elif s <= 9: return "integer"
                else: return "bigint"
            else: return "varchar"

        columns = []
        for name, data in zip(columnNames, columnData):
            columns.append('"%s" %s'%(name.lower(),convertData(*data)))

        cursor = database.handle.cursor()
        def execute(sql):
            print sql # verbose!
            #sql = unicode(sql,'utf-8')
            
            cursor.execute(str(sql))

        tablename = self.arguments["output table"].value
        out = 'CREATE TABLE "%s" (%s);\n' %(tablename,','.join(columns))
        execute(out)

        total = len(db)
        print total,"---------------------------------------------"
        count = 0
        for row in db:
            percent = count/total * 100
            print percent
            self.progress( percent )
            columns = []
            for field in row:
                columns.append( "'%s'"%field )
            out = 'INSERT INTO "%s" VALUES (%s);\n'%(tablename,','.join(columns))
            execute(out)
            count += 1

        database.handle.commit()
        self.progress(100)
        cursor.close()

#-------------------------------------------------------------------------------
# The rest is part of a script that was downloaded from here:
#   http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/362715
#
# Information:
#    Title: DBF reader and writer
#    Submitter: Raymond Hettinger (other recipes)
#    Last Updated: 2005/02/24
#    Version no: 1.5
#    Category: Databases 


import struct, datetime, decimal, itertools

def dbfreader(f):
    """Returns an iterator over records in a Xbase DBF file.

    The first row returned contains the field names.
    The second row contains field specs: (type, size, decimal places).
    Subsequent rows contain the data records.
    If a record is marked as deleted, it is skipped.

    File should be opened for binary reads.

    """
    # See DBF format spec at:
    #     http://www.pgts.com.au/download/public/xbase.htm#DBF_STRUCT

    numrec, lenheader = struct.unpack('<xxxxLH22x', f.read(32))    
    numfields = (lenheader - 33) // 32

    fields = []
    for fieldno in xrange(numfields):
        name, typ, size, deci = struct.unpack('<11sc4xBB14x', f.read(32))
        name = name.replace('\0', '')       # eliminate NULs from string   
        fields.append((name, typ, size, deci))
    yield [field[0] for field in fields]
    yield [tuple(field[1:]) for field in fields]

    terminator = f.read(1)
    assert terminator == '\r'

    fields.insert(0, ('DeletionFlag', 'C', 1, 0))
    fmt = ''.join(['%ds' % fieldinfo[2] for fieldinfo in fields])
    fmtsiz = struct.calcsize(fmt)
    for i in xrange(numrec):
        record = struct.unpack(fmt, f.read(fmtsiz))
        if record[0] != ' ':
            continue                        # deleted record
        result = []
        for (name, typ, size, deci), value in itertools.izip(fields, record):
            if name == 'DeletionFlag':
                continue
            if typ == "N":
                value = value.replace('\0', '').lstrip()
                if value == '':
                    value = 0
                elif deci:
                    value = decimal.Decimal(value)
                else:
                    value = int(value)
            elif typ == 'D':
                y, m, d = int(value[:4]), int(value[4:6]), int(value[6:8])
                value = datetime.date(y, m, d)
            elif typ == 'L':
                value = (value in 'YyTt' and 'T') or (value in 'NnFf' and 'F') or '?'
            else:
                #print type(value)," -> ",value#," -> ",unicode(value,'utf-8')
                #print value.decode('utf-8','replace')
                try: value.decode('utf_8','strict')
                except: value = value.decode('utf_8','ignore')

            result.append(value)
        yield result
